package com.hibase.core.lock.aop;

import com.hibase.core.lock.annotation.HibaseLock;
import com.hibase.core.lock.compoent.LockFailureStrategy;
import com.hibase.core.lock.compoent.LockKeyBuilder;
import com.hibase.core.lock.compoent.LockTemplate;
import com.hibase.core.lock.config.LockProperties;
import com.hibase.core.lock.entity.LockInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.util.StringUtils;

/**
 * 分布式锁aop处理器
 * @author chenfeng
 * @version 1.0
 * @date 2021/10/22 11:29
 * @des https://gitee.com/cheershi
 */
@Slf4j
@RequiredArgsConstructor
public class LockInterceptor implements MethodInterceptor {

    private final LockTemplate lockTemplate;

    private final LockKeyBuilder lockKeyBuilder;

    private final LockFailureStrategy lockFailureStrategy;

    private final LockProperties lockProperties;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 预防 使用其他aop组件时,aop切了两次.
        Class<?> cls = AopProxyUtils.ultimateTargetClass(invocation.getThis());
        if (!cls.equals(invocation.getThis().getClass())) {
            return invocation.proceed();
        }
        LockInfo lockInfo = null;
        try {
            // 得到切面方法上的注解
            HibaseLock lock = invocation.getMethod().getAnnotation(HibaseLock.class);
            // 生成前缀：默认前缀:name/默认前缀:全类名:方法名
            String prefix = lockProperties.getLockKeyPrefix() + ":";
            prefix += StringUtils.hasText(lock.name()) ? lock.name() :
                    invocation.getMethod().getDeclaringClass().getName() + invocation.getMethod().getName();
            // 生成key 支持spel表达式
            String key = prefix + "#" + lockKeyBuilder.buildKey(invocation, lock.keys());
            lockInfo = lockTemplate.lock(key, lock.expire(), lock.acquireTimeout(), lock.executor());
            if (null != lockInfo) {
                return invocation.proceed();
            }
            // lock failure
            lockFailureStrategy.onLockFailure(key, invocation.getMethod(), invocation.getArguments());
            return null;
        } finally {
            if (null != lockInfo) {
                final boolean releaseLock = lockTemplate.releaseLock(lockInfo);
                if (!releaseLock) {
                    log.error("releaseLock fail,lockKey={},lockValue={}", lockInfo.getLockKey(),
                            lockInfo.getLockValue());
                }
            }
        }
    }
}
